package models;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import models.msgs.*;
import play.Logger;
import play.mvc.*;
import play.libs.*;
import play.libs.F.*;
import scala.concurrent.Await;
import scala.concurrent.duration.Duration;
import akka.actor.*;
import static akka.pattern.Patterns.ask;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.ArrayNode;

import static java.util.concurrent.TimeUnit.*;

public class Player
{
    public enum Status {
	    Active, Archived, Inactive
	}
    
	public final String               name;
    public final ActorRef             strategyRef;
    public final ActorRef			  table;
    
    private Hand hand;
	private int coins;
	private Status status;
	private int noWinning;
	private Deck deck;
	private List<Integer> tempValue;
	private boolean valueUpdated;
	private int lastBet;

	
	/**
	 * Create player, initial status of player is Status.Active
	 * @param deck - deck, place from player can take cards
	 * @param coins - starting no of coins
	 */
    public Player(String _name, ActorRef _strategyRef, ActorRef _table, Deck _deck, int _coins)
    {
        name = _name;        
        strategyRef = _strategyRef;
        table = _table;
		deck = _deck;
		coins = _coins;
		noWinning = 0;
		status = Status.Active;
		valueUpdated = false;
		hand = new Hand();
		lastBet = 0;
		
		table.tell(new InformAll(new StringMessage("I've joined")), strategyRef);
    }
	
	/**
	 * Gets name of player
	 * @return name of player
	 */
	public String getName()
	{
		return name;
	}
	
	/**
	 * Sets status of player
	 * @param status status to set
	 */
	public void setStatus(Status status)
	{
		this.status = status;
	}
	
	/**
	 * Return current status of player
	 * @return status of player
	 */
	public Status getStatus()
	{
		return status;
	}
	
	/**
	 * Returns current no. of coins
	 * @return number of coins
	 */
	public int getCoins()
	{
		return coins;
	}
	
	public ActorRef getActorRef()
	{
		return strategyRef;
	}
	
	public void takeBet(int noCoins)
	{
		coins -= (noCoins-lastBet);
		lastBet = noCoins;
		strategyRef.tell(new GiveCoins(coins), table);
	}
	
	/**
	 * Gives coins to player
	 * @param noCoins number of coins to give 
	 */
	public void giveCoins(int noCoins)
	{
		coins += noCoins;
		strategyRef.tell(new GiveCoins(coins), table);
	}
	
	public void increaseNoWinnings()
	{
		noWinning++;
	}
	
	public int getNoWinning()
	{
		return noWinning;
	}
	
	public void prepareForNewBetting()
	{
		strategyRef.tell(new GiveStartCoins(coins), table);
		lastBet = 0;
	}
	
	/**
	 * Fills hand of player with cards from deck
	 */
	public void fillHand()
	{
		if(status != Status.Active)return;
		
		for(int i = 0; i < hand.getNumberOfNeededCards(); i++) {
			hand.giveCard(deck.takeCard());
		}
		strategyRef.tell(new GiveCards(hand.showCards()), table);
		valueUpdated = false;
	}
	
	/**
	 * Exchange cards in hand with the deck
	 * @throws Exception 
	 */
	public void exchangeCards() throws Exception
	{
		if(status != Status.Active)return;
		
		List<Card> cardsToExchange = (List<Card>) Await.result(ask(strategyRef, new GetCardsToExchange(), 60000), Duration.create(60, SECONDS));
		
		if(hand.getMaxNumberOfCardsToExchange() < cardsToExchange.size()) {
			throw new TablePlayerIncorrectImplementation("Some player wants to exchange too many cards");
		}
		
		Iterator<Card> iterC = cardsToExchange.iterator();
		while(iterC.hasNext()) {
			hand.takeCard(iterC.next());
			hand.giveCard(deck.takeCard());
		}
		
		strategyRef.tell(new GiveCards(hand.showCards()), table);
		valueUpdated = false;
	}
	
	/**
	 * Returns a wanted bet by the player
	 * @return no. of coins in the bet
	 * @throws Exception 
	 */
	public int retrieveBet() throws Exception
	{
		int bet = (int)Await.result(ask(strategyRef, new RetrieveBet(), 600000), Duration.create(60, SECONDS));
		if(status == Status.Active) return bet;
		return 0;
	}
	
	/**
	 * Return value of hand
	 * @return value of hand
	 */
	public List<Integer> getValue()
	{
		if(!valueUpdated) {
			tempValue = hand.getValue();
			valueUpdated = true;
		}
		return tempValue;
	}
	
	public List<Card> getCards()
	{
		return hand.showCards();
	}
}

